在上次我們完成了兩個最基礎的 GraphQL 查詢,接下來將繼續介紹 GraphQL 查詢的其他語法操作。
在之前的使用引數(Arguments)來查詢某個 id 的使用者時,我們是將 id 寫死在查詢上,如上圖,
如果我們想要動態設定引數的數值,這個時候就要使用 GraphQL 的變數(Variables)語法,以及設定對應的變數值,語法如下:
query UserQuery($userId: Int!) {
user(id: $userId) {
username
id
email
isActive
fullName
firstName
lastLogin
lastName
password
role
}
}
我們要在操作名稱旁邊宣告要使用$
加上變數名稱與對應的型別,像是上面宣告了$userId
使用Int!
型態,這樣就可以下面的查詢語法中使用了,也可以在宣告變數的地方設定預設值,例如:$userId: Int! = 9488
。
在 GraphiQL 頁面上,我們可以在 Variables 的區塊內編輯 JSON 格式資料,來幫我們的 GraphQL 變數設定對應值。
另外在 Variables 旁邊的 Headers 可以設定 HTTP Header,也是使用 JSON 格式編輯,例如下面增加 Authorization
:
{
"Authorization": "Bearer xxxxx.token.xxxxx.string.xxxx"
}
接著我們幫UserService
增加新的功能,再繼續學習期其他的查詢語法。
# app/services.py
import itertools
# ... 省略
class UserService:
# ... 省略
- def all_users(self, role: types.UserRole) -> typing.List[types.User]:
+ def all_users(
+ self,
+ role: typing.Optional[types.UserRole]= None,
+ ) -> typing.List[types.User]:
+ role_map = {
+ types.UserRole.NORMAL: self.normal_users,
+ types.UserRole.STAFF: self.staff_users,
+ types.UserRole.MANAGER: self.manager_users,
+ types.UserRole.ADMIN: self.admin_users,
+ }
+ if role:
+ return role_map[role]
- return list(
- self.normal_users + self.admin_users + self.staff_users + self.manager_users
- )
+ return list(itertools.chain.from_iterable(role_map.values()))
上面我們幫UserService.all_users
加上role:
引數(Arguments)用角色來篩選出對應的使用者列表。
這段程式碼的修改,主要是增加每個角色對應該角色的使用者列表的字典,當有傳入角色的引數時,就取得對應的使用者列表,則將所有角色的使用者列表透過itertools.chain.from_iterable
來回傳所有使用者列表。
圖上有標記 GraphiQL 頁面 Explorer 操作順序。
假設我們需要在同一個查詢內查詢同一個物件,這個時候我們使用別名(Aliases)來幫我們同一個查詢的物件不同的查詢結果另外命名,如下語法:
query UserListQuery {
adminUsers: users(role: ADMIN) {
email
firstName
fullName
id
isActive
lastLogin
lastName
password
role
username
}
normalUsers: users(role: NORMAL) {
email
firstName
fullName
id
isActive
lastLogin
lastName
password
role
username
}
}
在查詢語法的地方,在UserListQuery
內查詢兩種角色users
物件,我們分別用adminUsers
與normalUsers
作為別名,執行後可以得到兩個對應到別名的 JSON Key,以及對應的資料,另外別名無法使用 GraphiQL Explorer 設定出來,必須手動編輯語法。
app/services.py
完整程式碼:
# app/services.py
import itertools
import typing
from faker import Faker
from app import types
fake = Faker(["zh_TW", "en_US"])
def gen_user_info() -> (
typing.Tuple[
typing.Dict[str, typing.Any],
typing.Dict[str, typing.Any],
]
):
profile = fake.simple_profile()
user_info = dict(
id=fake.random_int(min=1),
username=profile["username"],
email=fake.ascii_safe_email(),
first_name=fake.first_name(),
last_name=fake.last_name(),
password=fake.lexify(text="??????????????????????????????"),
last_login=fake.date_time_this_century() if fake.random_digit() > 1 else None,
is_active=bool(fake.random_digit() > 1),
)
detail = dict(
birthdate=profile["birthdate"],
address=profile["address"],
phone=fake.phone_number(),
)
return user_info, detail
def gen_normal_user() -> types.User:
user_info, detail = gen_user_info()
user_info["role"] = types.UserRole.NORMAL
user_info["detail"] = types.NormalUserDetail(**detail)
return types.User(**user_info)
def gen_staff_user(department: str) -> types.User:
user_info, detail = gen_user_info()
user_info["role"] = types.UserRole.STAFF
detail["department"] = department
user_info["detail"] = types.StaffUserDetail(**detail)
return types.User(**user_info)
def gen_manager_user(
department: str,
subordinates: typing.List[types.User],
) -> types.User:
user_info, detail = gen_user_info()
user_info["role"] = types.UserRole.MANAGER
detail["department"] = department
detail["subordinates"] = subordinates
user_info["detail"] = types.ManagerUserDetail(**detail)
return types.User(**user_info)
def gen_admin_user() -> types.User:
user_info, _ = gen_user_info()
user_info["role"] = types.UserRole.ADMIN
user_info["detail"] = types.AdminUserDetail(
system_permissions=fake.random_choices(
elements=(
"read_users",
"read_user",
"write_user",
"update_user",
"delete_user",
),
)
)
return types.User(**user_info)
class UserService:
def __init__(self) -> None:
self.normal_users = [gen_normal_user() for _ in range(20)]
self.staff_users = []
self.manager_users = []
self.admin_users = [gen_admin_user() for _ in range(10)]
self.departments = [
"Human Resources",
"IT",
"Accounting",
"Finance",
"Marketing",
"Research",
"Development",
"Production",
]
self._gen_department_users()
def _gen_department_users(self) -> None:
for department in fake.random_choices(elements=self.departments):
staffs = [gen_staff_user(department) for _ in range(fake.random_digit())]
managers = [
gen_manager_user(department, staffs)
for _ in range(fake.random_int(min=0, max=5))
]
self.staff_users.extend(staffs)
self.manager_users.extend(managers)
def all_users(
self,
role: typing.Optional[types.UserRole] = None,
) -> typing.List[types.User]:
role_map = {
types.UserRole.NORMAL: self.normal_users,
types.UserRole.STAFF: self.staff_users,
types.UserRole.MANAGER: self.manager_users,
types.UserRole.ADMIN: self.admin_users,
}
if role:
return role_map[role]
return list(itertools.chain.from_iterable(role_map.values()))
def user(self, id: int) -> typing.Optional[types.User]:
for user in self.all_users():
if user.id == id:
return user
return None